/*
 * Copyright 2024 ywcai
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

`include "defines.v"
`timescale 1ns/1ps

module csr_reg(
	input	wire				clk,
	input	wire				rst_n,

	input	wire				wr_en_i,
	input	wire[`MemAddrBus]	wr_addr_i,
	input	wire[`RegDataBus]	wr_data_i,
	input	wire[`MemAddrBus]	rd_addr_i,

	output	wire				csr_mie_o,
	output	wire[`RegDataBus]	rd_data_o,
    output	wire[`RegDataBus]	csr_mtvec_o,
    output	wire[`RegDataBus]	csr_mepc_o,
    output	wire[`RegDataBus]	csr_mstatus_o
	);
 
    reg[`RegDataBus] cycle;
    reg[`RegDataBus] mtvec;
    reg[`RegDataBus] mcause;
    reg[`RegDataBus] mepc;
    reg[`RegDataBus] mie;
    reg[`RegDataBus] mstatus;
    reg[`RegDataBus] mscratch;

	assign csr_mie_o = mstatus[3];

    assign csr_mtvec_o = mtvec;
    assign csr_mepc_o = mepc;
    assign csr_mstatus_o = mstatus;

    always @(posedge clk) begin
        if (rst_n == `RESET_ENABLE) begin
            cycle <= `ZERO;
        end else begin
            cycle <= cycle + 1'b1;
        end
    end

	always @(posedge clk) begin
        if (rst_n == `RESET_ENABLE) begin
            mtvec <= `ZERO;
            mcause <= `ZERO;
            mepc <= `ZERO;
            mie <= `ZERO;
            mstatus <= `ZERO;
            mscratch <= `ZERO;
        end else begin
            if (wr_en_i == `ENABLE) begin
                case (wr_addr_i[11:0])
                    `CSR_MTVEC: begin
                        mtvec <= wr_data_i;
                    end
                    `CSR_MCAUSE: begin
                        mcause <= wr_data_i;
                    end
                    `CSR_MEPC: begin
                        mepc <= wr_data_i;
                    end
                    `CSR_MIE: begin
                        mie <= wr_data_i;
                    end
                    `CSR_MSTATUS: begin
                        mstatus <= wr_data_i;
                    end
                    `CSR_MSCRATCH: begin
                        mscratch <= wr_data_i;
                    end
                    default: begin

                    end
                endcase
            end
        end
    end

    // mem module read reg
    wire read_cycle = (rd_addr_i[11:0] == `CSR_CYCLE);
    wire read_mtvec = (rd_addr_i[11:0] == `CSR_MTVEC);
    wire read_mcause = (rd_addr_i[11:0] == `CSR_MCAUSE);
    wire read_mepc = (rd_addr_i[11:0] == `CSR_MEPC);
    wire read_mie = (rd_addr_i[11:0] == `CSR_MIE);
    wire read_mstatus = (rd_addr_i[11:0] == `CSR_MSTATUS);
    wire read_mscratch = (rd_addr_i[11:0] == `CSR_MSCRATCH);
	wire [`RegDataBus] csr_rd_data = ({`XLEN{read_cycle}} & cycle)
        | ({`XLEN{read_mtvec}} & mtvec)
        | ({`XLEN{read_mcause}} & mcause)
        | ({`XLEN{read_mepc}} & mepc)
        | ({`XLEN{read_mie}} & mie)
        | ({`XLEN{read_mstatus}} & mstatus)
        | ({`XLEN{read_mscratch}} & mscratch);
    assign rd_data_o = (wr_en_i & (rd_addr_i == wr_addr_i))
        ? wr_data_i : csr_rd_data;

endmodule
